
#include "FileReadBehaviorMP3.h"

using namespace std;

FileReadBehaviorMP3 :: FileReadBehaviorMP3() : IFileReadBehavior()
{	
	this->parser = new MP3Parser();
	// connect the signal to be forwarded to the Main Window Class
	connect(parser, SIGNAL(percentFinishedParsing(int)), this, SLOT(forwardSignalToProgBar(int)));
}
	
int FileReadBehaviorMP3 :: calculateWaveform ( SoundSample* parentSample, int width )
{
	unsigned char* dataBuffer;
	size_t buffer_size;
	
	dataBuffer = parser->parse(parentSample, buffer_size);
	
	if( dataBuffer == NULL)
	{
		cout << "Error parsing the mp3 file";
		return -1;
	}
		
	// bytes to read
	int bytesToRead = buffer_size;
	
	// if our widget is i.e. 800 pixels wide, we read 800 datasets in the file
	// we just look at one channel in case of a stereo file
	int span = ((bytesToRead) / (parentSample->getChannels().toInt() * (parentSample->getBitdepth().toInt()/8)) / width);
	
	// give the pointer the address of the first audio sample
	unsigned char* charPtr = dataBuffer;
	
	// allocate space for the plot data buffers
	int* plotDataUp = new int[width];
	int* plotDataDown = new int [width];
	
	// points to one sample, we need to make it int because it could be 24 bit per channel
	int* samplePtr = NULL;
	
	// the value that we need to shift the pointer around, 
	// calculated from the number of channels and the bitdepth
	int sampleJumpDistance = (parentSample->getChannels().toInt() * (parentSample->getBitdepth().toInt() >> 3));
	int sampleMask, signMask, fillMask;
	
	// both the file format and the processor works with Little Endian
	// This means that 0x12345678 would be 78 56 34 12 in memory
	if (parentSample->getBitdepth().toInt() == 8) {
		
		sampleMask = 0xff;
		
		// we have to check wether it's a negative number or not
		// 1000 0000
		signMask = 128;
		
		// mask to fill up with 1s in case of a negative number
		fillMask = 0xffffff00;
	}
	else if ( parentSample->getBitdepth().toInt() == 16)
	{
		sampleMask = 0xffff;
		
		// we have to check wether it's a negative number or not
		// 1000 0000 0000 0000
		signMask = 32768;
		
		// mask to fill up with 1s in case of a negative number
		fillMask = 0xffff0000;
	}
	else if ( parentSample->getBitdepth().toInt() == 24)
	{
		sampleMask = 0xffffff;
		
		// we have to check wether it's a negative number or not
		// 1000 0000 0000 0000 0000 0000
		signMask =  8388608;
		
		// mask to fill up with 1s in case of a negative number
		fillMask = 0xff000000;
	}
	else {
		cout << "No encoding set or encoding not supported";
	}

	
	// compute the minimum or maximum of a "span" of samples
	for ( int i=0; i < width; i++ )
	{
		int minimum = 0 , maximum = 0;
		int tempInt = 0, signInt = 0;
		
		for ( int j = 0; j < span; j++ )
		{
			samplePtr = (int*) charPtr;
			
			// we don't want to modify memory!!!
			tempInt = *samplePtr & sampleMask;
			
			// if the first bit of the resulting value after applying the mask
			// is 1, the value is negative, so we need to fill the left portion of our int with 1
			signInt = tempInt & signMask;
			
			if( signInt > 0)
			{
				// fill the left portion with 1s to remain negative numbers
				tempInt |= fillMask;
			}
			
			if ( tempInt > maximum )
			{
				maximum = tempInt;
			}
			else if ( tempInt < minimum)
			{
				minimum = tempInt;
			}
			
			charPtr +=  sampleJumpDistance;	// shift to next sample
		}
		
		plotDataUp[i]	= maximum;
		plotDataDown[i] = minimum;
	}
	
	// pass the data to the parent SoundSample object
	parentSample->setPlotDataUp(plotDataUp);
	parentSample->setPlotDataDown(plotDataDown);
	
	// free memory
	delete [] dataBuffer;
	
	return 0;
}
	
int FileReadBehaviorMP3 :: gatherMetaInformation( SoundSample* parentSample )
{
	parser->gatherID3Info(parentSample);
	return 0;
}

FileReadBehaviorMP3::~FileReadBehaviorMP3()
{
	delete parser;
	
}

MP3Parser* FileReadBehaviorMP3::getParser() const
{
	return this->parser;
}
	
// we need to forward the signal for the progress of the MP3 decoding operation
// to the mainwindow slot. This is used to display the progress bar.
void FileReadBehaviorMP3::forwardSignalToProgBar(int value)
{
	emit percentFinished(value);
}
